// +----------------------------------------------------------------------
// | Manong.Cloud [ 领酷码农云 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2020 http://www.manong.cloud All rights reserved.
// +----------------------------------------------------------------------
// | Author: 稻草人 <qinuoyun@qq.com>
// +----------------------------------------------------------------------
// | Member: 围棋 飘逸者 Loumt Silence24
// +----------------------------------------------------------------------
/**
 * 加载验证器
 */
import AutomaticValidator from './AutomaticValidator.js';

global.CommonModels = class CommonModels {
    /**
     * 是否开启检索
     * @type {boolean}
     */
    $account = true;

    /**
     * 模型标题
     * @type {boolean}
     */
    $title = '';

    /**
     * 当前场景
     * @type {string}
     */
    scenario = '';

    /**
     * 初始化构造方法
     * @return {[type]} [description]
     */
    constructor(ctx = null) {
        //用于判断变量是否存在，如果存在的情况下才进行数据处理
        if (typeof db != "undefined") {
            //原型链数据继承
            let DB = this._getTableModel();
            //处理原型链属性赋值
            let _obj = Object.getOwnPropertyNames(DB);
            for (let index in _obj) {
                let item = _obj[index];
                if (item !== 'constructor') {
                    //通过defineProperty来代替this.__proto__[item],在原型链添加数据
                    Object.defineProperty(this, item, {
                        value: DB[item],
                        enumerable: true,
                        writable: true,
                    });
                }
            }
            let selfProperty = Object.getOwnPropertyNames(Object.getPrototypeOf(CommonModels));
            //Object.getPrototypeOf(DB) 用来代替 __proto__ ,__proto__将被弃用
            //处理原型链数据赋值
            _obj = Object.getOwnPropertyNames(Object.getPrototypeOf(DB));
            for (let index in _obj) {
                let item = _obj[index];
                if (item !== 'constructor') {
                    Object.defineProperty(this, item, {
                        value: DB[item],
                        enumerable: true,
                        writable: true,
                    });
                }
            }
        }
        if (ctx) {
            //存储请求对象
            this.__ctx__ = ctx;
            if ($_GET['deny'] == 1 && ctx.state.user) {
                let { user } = ctx.state;
                this.setUser(user.id)
            }
        }
    }

    /**
     * 默认验证规则
     * @returns {{}}
     */
    rules() {
        return {}
    }

    /**
     * 默认场处理
     */
    scenarios() {
        return false
    }

    /**
     * 设置场景
     * @param scenario
     * @returns {CommonModels}
     */
    setScenario(scenario = '') {
        if (scenario && this.scenarios()) {
            if (this.scenarios()[scenario] != undefined) {
                this.scenario = this.scenarios()[scenario];
            }
        }
        return this;
    }

    /**
     * 获取表名称
     * @return {[type]} [description]
     */
    getTableName(tableName = null) {
        try {
            //1.获取表格名，并且判断是否添加表前缀
            tableName = tableName || this.tableName();
            if (tableName.indexOf("{{%") === 0) {
                tableName = tableName.match(/\{\{\%([\w]+)\}\}/);
                if (tableName[1]) {
                    let database = C("database");
                    if (database) {
                        tableName = tableName[1];
                    } else {
                        throw new Error('请在config.js中配置database');
                    }
                } else {
                    throw new Error('表名设置错误');
                }
            }
            return tableName;
        } catch (error) {
            throw error;
        }
    }

    /**
     * 获取表名称
     * @return {[type]} [description]
     */
    getClassName(tableName = null) {
        try {
            //1.获取表格名，并且判断是否添加表前缀
            tableName = tableName || this.tableName();
            if (tableName.indexOf("{{%") === 0) {
                tableName = tableName.match(/\{\{\%([\w]+)\}\}/);
                if (tableName[1]) {
                    tableName = tableName[1];
                } else {
                    throw new Error('表名设置错误');
                }
            }
            return tableName;
        } catch (error) {
            throw error;
        }
    }

    /**
     * 获取表结构模型
     * @return {[type]} [description]
     */
    _getTableModel() {
        try {
            //1.获取表格名，并且判断是否添加表前缀
            let tableName = this.getTableName();
            return db.name(tableName);
        } catch (error) {
            throw error;
        }
    }


    /**
     * 获取模型
     * @return {[type]} [description]
     */
    model() {
        let name = this.getTableName();
        return this.sequelize.models[name];
    }


    /**
     * 验证类
     */
    async validate(postData, rulesData = "") {
        let rulesObject = {};
        let scenario = this.scenario;
        if (scenario) {
            let _Object = {};
            scenario.forEach(key => {
                if (this.rules()[key]) {
                    _Object[key] = this.rules()[key];
                }
            })
            rulesObject = _Object;
        } else {
            rulesObject = this.rules();
        }
        //判断如果传入了自定义规格
        if (rulesData) {
            rulesObject = rulesData;
        }
        //读取需要验证的字段信息
        let rules = {};
        for (let key in postData) {
            if (rulesObject[key]) {
                rules[key] = rulesObject[key];
            }
        }
        //执行手动数据验证
        return new Promise(async (resolve, reject) => {
            let rules = {};
            for (let key in postData) {
                if (rulesObject[key]) {
                    rules[key] = rulesObject[key];
                }
            }
            const { valid, message } = await (new AutomaticValidator(rules, this)).validate(postData);
            //判断验证是否通过
            if (valid) {
                resolve(true)
            }
            //验证不过的情况
            else {
                reject(new Error(message));
            }
        })
    }


    /**
     * 处理字段信息-接口方法
     * @return {[type]} [description]
     */
    tableFields() {
        return {}
    }

    /**
     * 初始化表格名称
     * @return {[type]} [description]
     */
    tableName() {
        return "{{%" + this.constructor.name + "}}";
    }

    /**
     * 执行数据保存
     * @return {[type]} [description]
     */
    save() {
        // this.insert($_POST);
        console.log("执行了数据保存")
    }

    /**
     * 仅仅在模型下支持
     * @return {[type]} [description]
     */
    joinWith(model, where = {},include={}) {
        if (Array.isArray(model)) {
            for (let index in model) {
                let item = model[index];
                let _getAction = "get" + this._getModelName(item);
                if (typeof this[_getAction] === "function") {
                    let options = this[_getAction]();
                    if (where[item]) {
                        options["where"] = where[item];
                    }
                    if (include[item]) {
                        options["include"] = include[item];
                        options["include"]['name'] = this.getTableName(options["include"]['name']);
                    }
                }
            }
        }
        return this;
    }

    /**
     * 1:1关联查询
     * @return {Boolean} [description]
     */
    hasOne(name, field, relation = 'id', attributes = []) {
        if (is_array(relation)) {
            attributes = relation;
            relation = 'id';
        };
        let dbName = '';
        let asName = '';
        if (typeof name == 'string') {
            dbName = this.getTableName(name);
            asName = this.getClassName(name);
        } else {
            asName = name.as;
            name = name.name;
            dbName = this.getTableName(name);
        }
        return this.options['join'][asName] = {
            "type": 'hasOne',
            "name": dbName,
            "field": field,
            "relation": relation,
            "as": asName,
            "attributes": attributes
        }

    }

    /**
     * 1:1反向操作
     * @return {[type]} [description]
     */
    belongsTo(name, field, relation = 'id', attributes = []) {
        if (is_array(relation)) {
            attributes = relation;
            relation = 'id';
        }
        let dbName = '';
        let asName = '';
        if (typeof name == 'string') {
            dbName = this.getTableName(name);
            asName = this.getClassName(name);
        } else {
            asName = name.as;
            name = name.name;
            dbName = this.getTableName(name);
        }
        return this.options['join'][asName] = {
            "type": 'belongsTo',
            "name": dbName,
            "field": field,
            "relation": relation,
            "as": asName,
            "attributes": attributes
        }

    }

    /**
     * 1:2关联查询
     * @return {Boolean} [description]
     */
    hasMany(name, field, relation = 'id', attributes = []) {
        if (is_array(relation)) {
            attributes = relation;
            relation = 'id';
        }
        let dbName = '';
        let asName = '';
        let include = null;
        if (typeof name == 'string') {
            dbName = this.getTableName(name);
            asName = this.getClassName(name);
        } else {
            asName = name.as;
            name = name.name;
            dbName = this.getTableName(name);
        }
        return this.options['join'][asName] = {
            "type": 'hasMany',
            "name": dbName,
            "field": field,
            "relation": relation,
            "as": asName,
            "attributes": attributes
        }

    }

    /**
     * 关联查询条件
     * @return {Boolean} [description]
     */
    joinWhere(data) {
        this.options['join']['where'] = data;
        return this;
    }


    /**
     * 获取名称
     * @param  {[type]} name [description]
     * @return {[type]}      [description]
     */
    _getModelName(name) {
        return name.charAt(0).toUpperCase() + name.slice(1)
    }

    /**
     * 验证更新
     * @param postData
     * @param rules
     * @returns {Promise<unknown>}
     */
    validateUpdate(postData, rulesData = '', options = {}) {
        //判断如果验证规则不存在的情况
        let rulesObject = {};
        let scenario = this.scenario;
        if (scenario) {
            let _Object = {};
            scenario.forEach(key => {
                if (this.rules()[key]) {
                    _Object[key] = this.rules()[key];
                }
            })
            rulesObject = _Object;
        } else {
            rulesObject = this.rules();
        }
        //判断如果传入了自定义规格
        if (rulesData) {
            rulesObject = rulesData;
        }
        return new Promise(async (resolve, reject) => {
            //读取需要验证的字段信息
            let rules = {};
            for (let key in postData) {
                if (rulesObject[key]) {
                    rules[key] = rulesObject[key];
                }
            }
            const { valid, message, validator } = await (new AutomaticValidator(rules, this)).validate(postData);
            //判断验证是否通过
            if (valid) {
                const fields = Object.keys(this.tableFields());
                if (Array.isArray(fields) && fields.length > 0) {
                    let _postData = {};
                    fields.forEach((key) => {
                        if (postData[key]) {
                            _postData[key] = validator.getValue(key) || postData[key]
                        }
                    });
                    //执行数据更新操作
                    this.update(_postData, options).then(data => {
                        //合并数据对象，并返回
                        resolve(data)
                    }).catch(error => {
                        reject(error);
                    });
                } else {
                    reject(new Error("请正确设置模型tableFields"));
                }
            }
            //验证不过的情况
            else {
                reject(new Error(message));
            }
        })
    }

    /**
     * 验证创建数据
     * @param postData
     * @param rules
     * @returns {Promise<unknown>}
     */
    validateCreate(postData, rulesData = '', options = {}) {
        //判断如果验证规则不存在的情况
        let rulesObject = {};
        let scenario = this.scenario;
        if (scenario) {
            let _Object = {};
            scenario.forEach(key => {
                if (this.rules()[key]) {
                    _Object[key] = this.rules()[key];
                }
            })
            rulesObject = _Object;
        } else {
            rulesObject = this.rules();
        }
        //判断如果传入了自定义规格
        if (rulesData) {
            rulesObject = rulesData;
        }
        return new Promise(async (resolve, reject) => {
            let rules = {};
            for (let key in postData) {
                if (rulesObject[key]) {
                    rules[key] = rulesObject[key];
                }
            }
            const { valid, message, validator } = await (new AutomaticValidator(rules, this)).validate(postData);
            //判断验证是否通过
            if (valid) {
                const fields = Object.keys(this.tableFields());
                if (Array.isArray(fields) && fields.length > 0) {
                    let _postData = {};
                    fields.forEach((key) => {
                        //优化规则数据不存在的时候，不调用验证数据
                        if (Object.keys(rules).length) {
                            _postData[key] = validator.getValue(key) || postData[key]
                        } else {
                            _postData[key] = postData[key]
                        }
                    });
                    //执行数据写入操作
                    this.insert(_postData, options).then(data => {
                        //合并数据对象，并返回
                        resolve(data)
                    }).catch(error => {
                        reject(error);
                    });
                } else {
                    reject(new Error("请正确设置模型tableFields"));
                }
            }
            //验证不过的情况
            else {
                reject(new Error(message));
            }
        })
    }
}